package sg.edu.nus.iss.billing.controller;

import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import sg.edu.nus.iss.billing.BillingConstants;
import sg.edu.nus.iss.billing.dao.BaseDAO;
import sg.edu.nus.iss.billing.dao.DaoFactory;
import sg.edu.nus.iss.billing.exceptions.ControllerException;
import sg.edu.nus.iss.billing.model.account.Account;
import sg.edu.nus.iss.billing.model.account.Account.CustomerStatus;

/**
 * @author Yeo Hee Ling, Saravana, EngKhoon
 *
 */
public class CustAccountController {

	private Properties properties;
	
	public CustAccountController() {
		super();
		try {
			properties = new Properties();
			FileInputStream in = new FileInputStream(BillingConstants.PROPERTIES_CUSTOMER);
			properties.load(in);
			in.close();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public boolean registerCustAccount(String name, String nric, String addr, String postalCd, String contactNo, List<String> interests) throws ControllerException {
		//Validate the input data first.
		//If there's any error, just throw an exception, to be caught and displayed at the GUI level
		String errorMsg = validateInput(name, nric, addr, postalCd, contactNo, true);		
		if(!errorMsg.isEmpty()){
			String displayMsg = properties.getProperty("error.message.registerUnsuccessful.main");
			displayMsg += errorMsg.toString();
			throw new ControllerException(displayMsg);
		}
		
		try {
			BaseDAO<Account> accountDao = DaoFactory.getAccountDAO(BillingConstants.DATASOURCE_ACCOUNT);			
			Account account = new Account(generateAccountNo(), nric, name, addr, postalCd, contactNo, interests, CustomerStatus.ACTIVE);
			accountDao.saveElementByPK(account, "accountNo", account.getAccountNo());
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to register customer due to technical error. \nPlease contact your local administrator.");
		}
		return true;
	}
	
	public boolean updateCustAccount(Account account) throws ControllerException {
		//Validate the input data first.
		//If there's any error, just throw an exception, to be caught and displayed at the GUI level
		String errorMsg = validateInput(account.getCustomerName(), account.getCustomerNRIC(), 
				account.getBillingAddress(), account.getPostalCode(), account.getTelephoneNo(), false);		
		if(!errorMsg.isEmpty()){
			String displayMsg = properties.getProperty("error.message.updateUnsuccessful.main");
			displayMsg += errorMsg.toString();
			throw new ControllerException(displayMsg);
		}
		
		try {
			BaseDAO<Account> accountDao = DaoFactory.getAccountDAO(BillingConstants.DATASOURCE_ACCOUNT);
			
			accountDao.saveElementByPK(account, "accountNo", account.getAccountNo());
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to update customer due to technical error. \nPlease contact your local administrator.");
		}
		return true;
	}
	
	public List<Account> searchExistingCustAccount(String name, String nric, String accountNo, CustomerStatus status) throws ControllerException {
		List<Account> matchedAccounts = new ArrayList<Account>();
		
		try {
			BaseDAO<Account> accountDao = DaoFactory.getAccountDAO(BillingConstants.DATASOURCE_ACCOUNT);
			
			List<Account> accounts = accountDao.getElementList();
			if(accounts != null && accounts.size() > 0) {
				Iterator<Account> acctIter = accounts.iterator();
				while(acctIter.hasNext()) {
					boolean matchedName = true;
					boolean matchedNric = true;
					boolean matchedAccountNo = true;
					boolean matchedStatus = true;
					Account account = acctIter.next();
					
					if(accountNo != null) {
						int index = account.getAccountNo().toUpperCase().indexOf(accountNo.toUpperCase());
						if(index == -1)
							matchedAccountNo = false;
					}
					if(name != null) {
						int index = account.getCustomerName().toUpperCase().indexOf(name.toUpperCase());
						if(index == -1)
							matchedName = false;
					}
					if(nric != null) {
						int index = account.getCustomerNRIC().toUpperCase().indexOf(nric.toUpperCase());
						if(index == -1)
							matchedNric = false;
					}
					if(status != null) {	
						if(account.getStatus() != status)
							matchedStatus = false;
					}
					
					if(matchedName && matchedNric && matchedAccountNo && matchedStatus)
						matchedAccounts.add(account);
				}
			}
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to search customer due to technical error. \nPlease contact your local administrator.");
		}
		return matchedAccounts;
	}
	
	private String validateInput(String name, String nric, String addr, String postalCd, String contactNo, boolean isCreate) throws ControllerException {
		StringBuffer errorMsg = new StringBuffer();
		
		if(nric == null || "".equals(nric)) {
			errorMsg.append(properties.getProperty("error.message.nricMandatory"));
		}
		else {
			if(isCreate) {
				boolean doesCustomerExist = isCustAccountExist(nric);
				if(doesCustomerExist) {
					errorMsg.append(properties.getProperty("error.message.customerExist"));
					return errorMsg.toString();
				}
			}
		}
		
		if(name == null || "".equals(name))
			errorMsg.append(properties.getProperty("error.message.nameMandatory"));
		
		if(addr == null || "".equals(addr))
			errorMsg.append(properties.getProperty("error.message.addrMandatory"));
		
		if(postalCd == null || "".equals(postalCd)) {
			errorMsg.append(properties.getProperty("error.message.postalMandatory"));
		}
		else {
			try {
				Integer.parseInt(postalCd);
			}
			catch(NumberFormatException ex) {
				errorMsg.append(properties.getProperty("error.message.postalNonNumeric"));
			}
		}
		
		if(contactNo == null || "".equals(contactNo)) {
			errorMsg.append(properties.getProperty("error.message.contactMandatory"));
		}
		else {
			try {
				Integer.parseInt(contactNo);
			}
			catch(NumberFormatException ex) {
				errorMsg.append(properties.getProperty("error.message.contactNonNumeric"));
			}
		}
		return errorMsg.toString();
	}

	private boolean isCustAccountExist(String nric) throws ControllerException {
		boolean customerExist = false;
		
		try {
			BaseDAO<Account> accountDao = DaoFactory.getAccountDAO(BillingConstants.DATASOURCE_ACCOUNT);
			
			List<Account> accounts = accountDao.getElementList();
			if(accounts != null && accounts.size() > 0) {
				Iterator<Account> iter = accounts.iterator();
				while(iter.hasNext()) {
					Account account = iter.next();
					
					if(nric.equalsIgnoreCase(account.getCustomerNRIC())) {
						customerExist = true;
						break;
					}
				}
			}
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to register customer due to technical error. \nPlease contact your local administrator.");
		}
		return customerExist;
	}
	
	private String generateAccountNo() throws Exception {
		DateFormat df = new SimpleDateFormat("yyyyMMdd");
		boolean acctExist = false;
		String key = "";
		String acctNo = "BSED-";
		String currentDate = df.format(System.currentTimeMillis());
		String num[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
		
		BaseDAO<Account> accountDao = DaoFactory.getAccountDAO(BillingConstants.DATASOURCE_ACCOUNT);
		
		while(!acctExist) {
			for(int n = 0; n < 3; n++) {
				int index = (int)(Math.random() * num.length);
				key += num[index];
			}
			acctNo += currentDate + "-" + key;
			
			List<Account> accounts = accountDao.getElementListByAttribute("accountNo", acctNo);
			if(accounts != null && accounts.size() > 0)
				acctExist = false;
			else
				acctExist = true;
		}
		return acctNo;
	}
	public boolean updateSubscription(Account account) throws ControllerException {
		try {
			if(account != null) {
				BaseDAO<Account> accountDao = DaoFactory.getAccountDAO(BillingConstants.DATASOURCE_ACCOUNT);
				accountDao.saveElementByPK(account, "accountNo", account.getAccountNo());
			}
			else {
				return false;
			}
			return true;
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to update subscription due to technical error. \nPlease contact your local administrator.");
		}
	}
}
